// Copyright 2019 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
#ifndef PLATFORM_DARWIN_IOS_LYNX_PUBLIC_LYNXVIEW_H_
#define PLATFORM_DARWIN_IOS_LYNX_PUBLIC_LYNXVIEW_H_

#import <UIKit/UIKit.h>

#import <Lynx/JSModule.h>
#import <Lynx/LUIBodyView.h>
#import <Lynx/LynxBooleanOption.h>
#import <Lynx/LynxConfigInfo.h>
#import <Lynx/LynxExtraTiming.h>
#import <Lynx/LynxGenericResourceFetcher.h>
#import <Lynx/LynxLifecycleDispatcher.h>
#import <Lynx/LynxLoadMeta.h>
#import <Lynx/LynxMediaResourceFetcher.h>
#import <Lynx/LynxPerformance.h>
#import <Lynx/LynxScrollListener.h>
#import <Lynx/LynxTemplateData.h>
#import <Lynx/LynxTheme.h>
#import <Lynx/LynxTouchEvent.h>
#import <Lynx/LynxUIListProtocol.h>
#import <Lynx/LynxUpdateMeta.h>
#import <Lynx/LynxViewBuilder.h>
#import <Lynx/LynxViewClient.h>
#import <Lynx/LynxViewClientV2.h>
#import <Lynx/LynxViewEnum.h>
#import "LynxImageFetcher.h"

@class LynxTemplateRender;
@class LynxContext;
@protocol LynxBaseInspectorOwner;

/**
 * @apidoc
 * @brief Similar to WebView in native developing. Renders bundle within host application’s context.
 */
@interface LynxView : UIView <LUIBodyView>

#pragma mark - Property

/*!
 The URL of the resource to load。

 @see loadTemplateFromURL:
 @see loadTemplate:withURL:
 @see loadTemplateFromURL:initData:
 @see loadTemplate:withURL:initData:
 */
@property(nonatomic, readonly, nullable) NSString* url;

/*!
 Unique identifier generated when LynxView is added to a LynxViewGroup,
 assigned by the view group's ID generation mechanism.

 Return 0 if the LynxView is not bound to any LynxViewGroup.

 @see LynxViewGroup
 */
@property(nonatomic, readwrite, assign) NSInteger lynxViewId;

/*!
 If `enableAsyncDisplay` is YES, sub-nodes' contents will be drawn asynchronously.
 Set `enableAsyncDisplay` is NO if it is not wanted. Default to YES.
 */
@property(nonatomic, readwrite, assign) BOOL enableAsyncDisplay;

// Layout, must call invalidateIntrinsicContentSize after change layout props
// If you use view.frame to set view frame, the layout mode will all be
// specified
@property(nonatomic, assign) LynxViewSizeMode layoutWidthMode;
@property(nonatomic, assign) LynxViewSizeMode layoutHeightMode;
@property(nonatomic, assign) CGFloat preferredMaxLayoutWidth;
@property(nonatomic, assign) CGFloat preferredMaxLayoutHeight;
@property(nonatomic, assign) CGFloat preferredLayoutWidth;
@property(nonatomic, assign) CGFloat preferredLayoutHeight;

// TODO(chennengshi):When
// layoutWidthMode/layoutHeightMode/preferredLayoutWidth/preferredLayoutHeight properties are
// migrated to LynxViewBuilder, enableAutoLayout will also be migrated to LynxViewBuilder
@property(nonatomic, assign) bool enableAutoLayout;

@property(nonatomic, weak, nullable) id<LynxImageFetcher> imageFetcher;
@property(nonatomic, weak, nullable) id<LynxScrollListener> scrollListener;
@property(nonatomic, weak, nullable) id<LynxListLayoutProtocol> customizedListLayout;
@property(nonatomic, readonly, nullable) id<LynxBaseInspectorOwner> baseInspectorOwner;
@property(nonatomic, strong, nullable) NSArray<UIScrollView*>* nestedScrollViewsChain;

@property(nonatomic, readwrite) BOOL catchAllException;

- (nullable id<LynxResourceFetcher>)resourceFetcher;
- (void)setResourceFetcher:(nullable id<LynxResourceFetcher>)resourceFetcher;

#pragma mark - Init

- (nonnull instancetype)init;
- (nonnull instancetype)initWithFrame:(CGRect)frame;
- (nonnull instancetype)initWithBuilderBlock:(LynxViewBuilderBlock)builder;
- (instancetype _Nullable)initWithoutRender;

- (void)initLifecycleDispatcher;

#pragma mark - Clean

// clear
- (void)clearForDestroy;

- (void)resetViewAndLayer;

#pragma mark - Template

/**
 * @apidoc
 * @brief Using [`LynxLoadMeta`](../lynx-load-meta.mdx) to render `LynxView`,
 * it is the main entrance for the client to load `Lynx` templates.
 * @param meta Lynx template loads metadata, and developers use this data structure
 * to specify optional data loaded by the template.
 */
- (void)loadTemplate:(nonnull LynxLoadMeta*)meta;
- (void)loadTemplateFromURL:(nonnull NSString*)url;
- (void)loadTemplateFromURL:(nonnull NSString*)url initData:(nullable LynxTemplateData*)data;
- (void)loadTemplate:(nonnull NSData*)tem withURL:(nonnull NSString*)url;
- (void)loadTemplate:(nonnull NSData*)tem
             withURL:(nonnull NSString*)url
            initData:(nullable LynxTemplateData*)data;

/**
 * Load LynxView with a pre-decoded template.js
 * Load LynxView with a pre-decoded template.js with [LynxTemplateBundle initWithTemplate], it can
 *be used for LynxTemplateBundle reuse.
 *
 *@param bundle The pre-decoded template.js with [LynxTemplateBundle initWithTemplate].
 *@param url The url of lynx template.
 *@param data The initData to be used when render first screen.
 */
- (void)loadTemplateBundle:(nonnull LynxTemplateBundle*)bundle
                   withURL:(nonnull NSString*)url
                  initData:(nullable LynxTemplateData*)data;

/**
 * Load ssr data.
 * SSR data is a kind of file format which is generated by server runtime of Lynx on server side.
 * The data represented the objects to be rendered on the page.
 * And with ssr data, the first screen will be rendered extremely fast.
 * But the ssr data doesn't contains the logic of page, the page won't be interactable if rendered
 * only with ssr data. To make the lynx view interactable, call ssrHydrate api with the page
 * template to attach the events.
 *
 * @param tem The binary ssr data
 * @param url The url of ssr data
 * @param data The data to be forwarded to the placeholders placed when rendering on server.
 */
- (void)loadSSRData:(nonnull NSData*)tem
            withURL:(nonnull NSString*)url
           initData:(nullable LynxTemplateData*)data;
- (void)loadSSRDataFromURL:(nonnull NSString*)url initData:(nullable LynxTemplateData*)data;

/**
 * Attach page logic to a LynxView rendered with loadSSRData.
 * Calling this api will make LynxView rendered with SSR data interactable and behave just like a
 * normal lynxview.
 *
 * @param tem The binary of the template
 * @param url The url of the template
 * @param data The init data of the template
 */
- (void)ssrHydrate:(nonnull NSData*)tem
           withURL:(nonnull NSString*)url
          initData:(nullable LynxTemplateData*)data;
- (void)ssrHydrateFromURL:(nonnull NSString*)url initData:(nullable LynxTemplateData*)data;

/**
 * Normally, updateData before loadTemplate does not take effect. If you want to cache the data
 * updated before loadTemplate and use it together when loadTemplate, please set EnablePreUpdateData
 * option in LynxViewBuilder.
 */
- (void)updateDataWithString:(nullable NSString*)data;
- (void)updateDataWithString:(nullable NSString*)data processorName:(nullable NSString*)name;
- (void)updateDataWithTemplateData:(nullable LynxTemplateData*)data;
- (void)updateDataWithDictionary:(nullable NSDictionary<NSString*, id>*)data;
- (void)updateDataWithDictionary:(nullable NSDictionary<NSString*, id>*)data
                   processorName:(nullable NSString*)name;

/**
 * @apidoc
 * @brief Using [LynxUpdateMeta](/api/lynx-native-api/lynx-update-meta) to update `LynxView`,
 * it is the main entrance for the client to update template data.
 * @param meta Lynx template update metadata, developers use this data structure to
 * specify the optional data content of the update template.
 */
- (void)updateMetaData:(nonnull LynxUpdateMeta*)meta;

/**
 * If data is nil or not legal data, global props will not be updated
 */
- (void)updateGlobalPropsWithTemplateData:(nonnull LynxTemplateData*)data;
- (void)updateGlobalPropsWithDictionary:(NSDictionary<NSString*, id>* _Nonnull)data;

/**
 * Clear current data and update with the given data. If you want to clear the current data only,
 * you can pass an Empty LynxTemplateData
 */
- (void)resetDataWithTemplateData:(nonnull LynxTemplateData*)data;

/**
 * Reload template with data or also with globalProps.
 */
- (void)reloadTemplateWithTemplateData:(nonnull LynxTemplateData*)data;
/**
 * @param globalProps default : nil. Global props will be reset by new nonnull globalProps. Empty
 * globalProps could overwrite the original props, but nil object will do nothing.
 */
- (void)reloadTemplateWithTemplateData:(nonnull LynxTemplateData*)data
                           globalProps:(nullable LynxTemplateData*)globalProps;

#pragma mark - Storage
- (void)setSessionStorageItem:(nonnull NSString*)key
             withTemplateData:(nullable LynxTemplateData*)data;

- (void)getSessionStorageItem:(nonnull NSString*)key
                 withCallback:(void (^_Nullable)(id<NSObject> _Nullable))callback;

- (double)subscribeSessionStorage:(nonnull NSString*)key
                     withCallback:(void (^_Nullable)(id<NSObject> _Nullable))callback;

- (void)unSubscribeSessionStorage:(nonnull NSString*)key withId:(double)callbackId;

#pragma mark - View

/**
 * EXPERIMENTAL API!
 * Updating the screen size for lynxview.
 * Updating the screen size does not trigger a re-layout, You should trigger  a re-layout by
 * yourself. It will be useful for the screen size change, like screen rotation. it can make some
 * css properties based on rpx shows better. Multiple views are not supported with different
 * settings!
 * @param width (dp) screen width
 * @param height (dp) screen screen
 */
- (void)updateScreenMetricsWithWidth:(CGFloat)width height:(CGFloat)height;

/**
 * @apidoc
 * @brief Changing the font scaling ratio in client settings
 * will automatically change the text size.
 * @param scale The new font scaling ratio.
 */
- (void)updateFontScale:(CGFloat)scale;

/**
 * Pause and resume the layout animation of lynxview.
 */
- (void)pauseRootLayoutAnimation;
- (void)resumeRootLayoutAnimation;

/**
 * Called during cell reuse, in prepareForReuse, and on successful reuse, call restart.
 */
- (void)resetAnimation;
- (void)restartAnimation;

// Set the theme and automatically trigger a UI refresh
- (void)setTheme:(nonnull LynxTheme*)theme;
// Get the current theme setting
- (nullable LynxTheme*)theme;

- (void)setNeedPendingUIOperation:(BOOL)needPendingUIOperation;

- (void)triggerLayout;

- (nullable LynxUI*)findUIByIndex:(int)index;

/**
 * @apidoc
 * @brief Find `UIView` based on name attribute.
 * @param name The name attribute of the view specified by the front end in the layout file.
 * @return The `UIView` node corresponding to the name attribute.
 */
- (nullable UIView*)findViewWithName:(nonnull NSString*)name;

/**
 * @apidoc
 * @brief Find `LynxUI` nodes based on the name attribute.
 * @param name The name attribute of the view specified by the front end in the layout file.
 * @return The `LynxUI` node corresponding to the name attribute.
 */
- (nullable LynxUI*)uiWithName:(nonnull NSString*)name;

/**
 * @apidoc
 * @brief Find `LynxUI` nodes based on the idSelector attribute.
 * @param idSelector The id attribute of the view specified by the front end in the layout file.
 * @return The `LynxUI` node corresponding to the idSelector attribute.
 */
- (nullable LynxUI*)uiWithIdSelector:(nonnull NSString*)idSelector;

/**
 * @apidoc
 * @brief Find `UIView` based on `idSelector` property.
 * @param idSelector The id attribute of the view specified by the front end in the layout file.
 * @return The `UIView` node corresponding to the idSelector attribute.
 */
- (nullable UIView*)viewWithIdSelector:(nonnull NSString*)idSelector;

/**
 * @apidoc
 * @brief Register lifecycle event observer for `LynxView`.
 * @param lifecycleClient The structure implemented by the client and registered to the `LynxView`
 * instance is used to obtain the callbacks of each process in the `LynxView` lifecycle.
 */
- (void)addLifecycleClient:(nonnull id<LynxViewBaseLifecycle>)lifecycleClient;

- (void)removeLifecycleClient:(nonnull id<LynxViewBaseLifecycle>)lifecycleClient;

- (void)updateViewportWithPreferredLayoutWidth:(CGFloat)preferredLayoutWidth
                         preferredLayoutHeight:(CGFloat)preferredLayoutHeight;

/**
 * @apidoc
 * @brief The client updates the `LynxView` view size.
 * @param preferredLayoutWidth Current `LynxView` width.
 * @param preferredLayoutHeight Current `LynxView` height.
 * @param needLayout Whether layout needs to be triggered.
 */
- (void)updateViewportWithPreferredLayoutWidth:(CGFloat)preferredLayoutWidth
                         preferredLayoutHeight:(CGFloat)preferredLayoutHeight
                                    needLayout:(BOOL)needLayout;

#pragma mark - Setter & Getter

- (void)setEnableTextNonContiguousLayout:(BOOL)enableTextNonContiguousLayout;
- (BOOL)enableTextNonContiguousLayout;

- (void)setEnableLayoutOnly:(BOOL)enableLayoutOnly;

- (void)setEnableSyncFlush:(BOOL)enableSyncFlush;

/**
 * The natural size for the Lynxview, considering the content with the
 * size and size mode of LynxView.
 *
 * @see layoutWidthMode
 * @see layoutHeightMode
 * @see preferredMaxLayoutWidth
 * @see preferredMaxLayoutHeight
 * @see preferredLayoutWidth
 * @see preferredLayoutHeight
 */
- (CGSize)intrinsicContentSize;

- (LynxLifecycleDispatcher* _Nullable)getLifecycleDispatcher;

#pragma mark - Event

- (void)onEnterForeground;
- (void)onEnterBackground;

/**
 * @apidoc
 * @brief Send global events to the front end through the client,
 * and the front end can listen to the event through `GlobalEventEmitter`.
 * @param name Global event name.
 * @param params Parameters carried by global events.
 */
- (void)sendGlobalEvent:(nonnull NSString*)name withParams:(nullable NSArray*)params;
- (void)sendGlobalEventToLepus:(nonnull NSString*)name withParams:(nullable NSArray*)params;

/**
 * send global event to lepus runtime(for lynx air)
 *
 * @param name event name
 * @param params context for event
 */
- (void)triggerEventBus:(nonnull NSString*)name withParams:(nullable NSArray*)params;

- (void)sendTouchEvent:(nullable LynxTouchEvent*)event;

#pragma mark - Get Info

/**
 * **EXPERIMENTAL API**
 *
 * Method to acquire current LynxView card data by Key.
 * Called IN **UI Thread**.
 *
 * Attentions: should be called after lynxview template loaded.
 * @param keys keys to be acquired.
 */
- (NSDictionary* _Nullable)getPageDataByKey:(nonnull NSArray*)keys;

- (nullable JSModule*)getJSModule:(nonnull NSString*)name;

- (nonnull LynxContext*)getLynxContext;

- (LynxThreadStrategyForRender)getThreadStrategyForRender;

- (BOOL)isLayoutFinish;

/**
 Method to acquire current LynxView card data.
 Called IN UI Thread.
 Attentions: should be called in ui thread & be called after LynxView template loaded.
 */
- (NSDictionary* _Nullable)getCurrentData;

- (float)rootWidth;

- (float)rootHeight;

#pragma mark - Timing & Report

/**
 * @apidoc
 * @brief Supplement key performance data before loading the Lynx page.
 * @param timing Used to supplement key performance data before loading the Lynx page.
 */
- (void)setExtraTiming:(LynxExtraTiming* _Nonnull)timing;

- (nullable NSDictionary*)getAllTimingInfo;

- (void)setExtraTimingWithDictionary:(NSDictionary* _Nonnull)timing;

/// Set whether to enable long task monitor.
///
/// @param enabled Whether to enable long task monitor.
/// Pass LynxBooleanOptionUnset to use the default behavior, i.e. the env value
/// of `enable_long_task_timing`(injected via the LynxTrailService).
/// Pass LynxBooleanOptionTrue to enable long task monitor.
/// Pass LynxBooleanOptionFalse to disable long task monitor.
///
/// @note This method is only effective when PageConfig is not configured with
/// kEnableLongTaskTiming.
- (void)setLongTaskMonitorEnabled:(LynxBooleanOption)enabled;

/// Set whether to enable fluency metics collection.
///
/// @param enabled Whether to enable fluency metics collection.
/// Pass LynxBooleanOptionUnset to use the default behavior, i.e. the env value
/// of `ENABLE_FLUENCY_TRACE`(injected via the LynxTrailService).
/// Pass LynxBooleanOptionTrue to enable fluency metrics collection.
/// Pass LynxBooleanOptionFalse to disable fluency metrics collection.
///
/// @note This method is only effective when PageConfig is not configured with
/// kEnableLynxScrollFluency.
- (void)setFluencyTracerEnabled:(LynxBooleanOption)enabled;

/// Put parameters for reporting events, overriding old values if the parameters already
/// exist.
/// - Parameter params: common parameters for report events, these values will be merged with params
/// of LynxEventReporter.onEvent.
- (void)putParamsForReportingEvents:(NSDictionary<NSString*, id>* _Nonnull)params;

#pragma mark - Multi Thread

- (void)runOnTasmThread:(dispatch_block_t _Nonnull)task;

- (void)syncFlush;

- (void)attachEngineToUIThread;
- (void)detachEngineFromUIThread;

#pragma mark - Component

/**
 * Preload lazy bundles with urls.
 * If you want to improve the efficiency of the first-screen display of lazy bundles, you can
 * preload them.
 */
- (void)preloadDynamicComponents:(nonnull NSArray*)urls
    API_DEPRECATED_WITH_REPLACEMENT("LynxTemplateResourceFetcher", ios(6.0, API_TO_BE_DEPRECATED));

/**
 * Register lazy bundles with TemplateBundle.
 * Use the parsed TemplateBundle to preload a lazy bundle, making lazy bundle ready in
 * a flash
 * @return Whether the bundle is successfully registered into lynx view. Reasons for failure may
 * include: the url or bundle is invalid; the bundle does not come from a lazy bundle
 * template.
 * @param url url of lazy bundle
 * @param bundle parsed bundle of lazy bundle
 */
- (BOOL)registerDynamicComponent:(nonnull NSString*)url
                          bundle:(nonnull LynxTemplateBundle*)bundle
    API_DEPRECATED_WITH_REPLACEMENT("LynxTemplateResourceFetcher", ios(6.0, API_TO_BE_DEPRECATED));

#pragma mark - Bytecode

/**
 * Enable user bytecode. Controls bytecode for files other than lynx_core.js, such as
 * app-service.js.
 *
 * @param enableUserBytecode if bytecode should bw enabled.
 * @param url Source url of the template.
 *            SourceUrl will be used to mark js files in bytecode.
 *            If the js file changes while url remains the same, code
 *            cache knows the js file is updated and will remove the
 *            cache generated by the former version of the js file.
 */
- (void)setEnableUserBytecode:(BOOL)enableUserBytecode url:(nonnull NSString*)url;

#pragma mark - Preload

- (void)attachTemplateRender:(LynxTemplateRender* _Nullable)templateRender;

- (void)processRender;

- (void)detachRender;

- (void)startLynxRuntime;

#pragma mark - Deprecated

/*!
 If isUIRunningMode return true, it means that the template assembler will work on ui thread
 completely including layout operation. Otherwise template assembler will work on layout thread.

 If you want LynxView behavior similar to native view such as layout children when layoutSubviews
 happens, it's better to return true to get better visual effect.

 It's better to return false if the template is too heavy so that getting better performance.
 */
@property(nonatomic, readonly) BOOL isUIRunningMode
    __attribute__((deprecated("try to set 'threadStrategyForRendering' if you want to change the "
                              "thread strategy for rendering")));

/*!
 Through this client, you can get callbacks for some key nodes in the
 running process of LynxView, such as page loading completion, etc.
 */
@property(nonatomic, weak, nullable) id<LynxViewClient> client
    __attribute__((deprecated("use xxxxFetcher instead, and use LynxViewLifecycle to "
                              "implement lifecycle methods")));

/**
 * If @see isUIRunningMode, you can get intrinsicContentSize immediately
 * after call invalidateIntrinsicContentSize. If not you can observe
 * the change of intrinsicContentSize through @see LynxViewClient
 */
- (void)invalidateIntrinsicContentSize __attribute__((deprecated("Use `triggerLayout` instead.")));

- (void)setEnableRadonCompatible:(BOOL)enableRadonCompatible
    __attribute__((deprecated("Radon diff mode can't be close after lynx 2.3.")));

- (void)requestLayoutWhenSafepointEnable;

- (void)setGlobalPropsWithTemplateData:(nonnull LynxTemplateData*)data
    __attribute__((deprecated("Use `updateGlobalPropsWithTemplateData` instead.")));
- (void)setGlobalPropsWithDictionary:(NSDictionary<NSString*, id>* _Nonnull)data
    __attribute__((deprecated("Use `updateGlobalPropsWithDictionary` instead.")));

// prefer using `findViewWithName:` than `viewWithName:`.
// `viewWithName:` will be marked deprecated in 1.6
- (nullable UIView*)viewWithName:(nonnull NSString*)name;

- (nullable NSString*)cardVersion __attribute__((deprecated("Use `lynxReportInfo` instead.")));

- (nullable LynxPerformance*)forceGetPerf
    __attribute__((deprecated("Use `LynxViewLifecycle didReceivexxx` instead.")));

- (void)processLayout:(nonnull NSData*)tem
              withURL:(nonnull NSString*)url
             initData:(nullable LynxTemplateData*)data __attribute__((deprecated(
                          "Use `loadTemplate:LynxLoadMeta` with "
                          "LynxLoadOption(LynxLoadOptionProcessLayoutWithoutUIFlush) instead.")));

- (void)processLayoutWithTemplateBundle:(nonnull LynxTemplateBundle*)bundle
                                withURL:(nonnull NSString*)url
                               initData:(nullable LynxTemplateData*)data
    __attribute__((
        deprecated("Use `loadTemplate:LynxLoadMeta` with "
                   "LynxLoadOption(LynxLoadOptionProcessLayoutWithoutUIFlush) instead.")));

// Calculate the width and height of view but do not create ui views.
// Call the processRender func to create ui views of this page.
- (void)processLayoutWithSSRData:(nonnull NSData*)tem
                         withURL:(nonnull NSString*)url
                        initData:(nullable LynxTemplateData*)data
    __attribute__((
        deprecated("Use `loadTemplate:LynxLoadMeta` with "
                   "LynxLoadOption(LynxLoadOptionProcessLayoutWithoutUIFlush) instead.")));

- (nonnull LynxConfigInfo*)lynxConfigInfo;

- (LynxTemplateData* _Nullable)getTemplateData;

@end

#endif  // PLATFORM_DARWIN_IOS_LYNX_PUBLIC_LYNXVIEW_H_
